# udp_http_client_serv00
vv='V13.20240906'
#使用前记得切换测试模式正式模式
import socket
import time
import _thread
import struct
from machine import reset
from machine import Pin,Signal
try:    #当esp32c3未安装urequests时,先进行自动安装该库
    import urequests
except:
    import upip
    upip.install('micropython-urequests')
    import urequests

print('udp_http_client',vv)
######################################################################

#########正式环境#########
#初始化GPIO
gpio1=Signal(Pin(2, Pin.OPEN_DRAIN), invert=True) #左1  #低电平触发继电器所以用开漏模式增强
gpio2=Signal(Pin(3, Pin.OPEN_DRAIN), invert=True) #左2  #低电平触发继电器所以用开漏模式增强
#gpio2=Signal(Pin(3, Pin.OUT), invert=True)  #小灯
gpio3=Signal(Pin(10, Pin.OUT), invert=True)  #左3   #高电平触发
gpio4=Signal(Pin(8, Pin.OUT), invert=True)  #左4    #高电平触发

gpio5=Signal(Pin(7, Pin.OPEN_DRAIN), invert=True) #左5  #低电平触发继电器所以用开漏模式增强
#gpio6=Signal(Pin(11, Pin.OUT), invert=True)  #左4    #高电平触发
gpio6=Signal(Pin(5, Pin.OPEN_DRAIN), invert=True) #左6  #低电平触发继电器所以用开漏模式增强

#固定两个自带的led的电平
led12=Signal(Pin(12, Pin.OUT), invert=False) #服务器电源
led13=Signal(Pin(13, Pin.OUT), invert=False) #服务器开机键
led12.off()
led13.off()
#初始化继电器状态
gpio1.off()
gpio2.off()
gpio3.off()
gpio4.off()
gpio5.off()
gpio6.off()

device_name='esp32c3_led_yedeng'
server_addr=('31.186.87.205',58147)
# server_addr_backup=(('192.168.8.188',58147))
yedeng_model=0
cl_stat=0   #窗帘状态
ydbt_stat=0 #小夜灯按钮状态
######################################################################

#固件更新相关

def update_firmware():  #   强制更新(直接更新)
    with open('main.py','w') as f:
        cloud_firmware=urequests.get(url='https://gitee.com/brotherwang/test/raw/master/esp32c3_led_yedeng/update_firmware.py').text
        f.write(cloud_firmware) #更新固件
    with open('firmware_version.txt','w') as f: #更新固件版本号
        f.write(str(eval(urequests.get(url='https://gitee.com/brotherwang/test/raw/master/esp32c3_led_yedeng/update_version.py').text)))
        print('固件更新成功')
        print('reboot')
    
    time.sleep(2)   #确保文件正常保存
    reset() #重启

def get_version():  #版本获取,如失败则强制更新
    try:
        with open('firmware_version.txt','r') as f:
            local_version=eval(f.read()).get('version')
            if local_version:
                udp.sendto('#v#{}#{}'.format(device_name,local_version).encode(),addr)
        return local_version
    except:
        print('获取本地版本失败,请检查版本文件,程序尝试更新至最新')
        return 999

def auto_update_firmware(): #智能更新,版本获取,先判断再更新,如失败则强制更新
    try:
        with open('firmware_version.txt','r') as f:
            local_version=eval(f.read()).get('version')
    except: 
        update_firmware()
    cloud_version=eval(urequests.get(url='https://gitee.com/brotherwang/test/raw/master/esp32c3_led_yedeng/update_version.py').text).get('version')
    if cloud_version > local_version:
        update_firmware()

###############
#wol唤醒
#micropython通过单片机进行wol唤醒PC计算机_20230606

def wake_on_lan(mac_address):
    if len(mac_address) == 12:
        pass
    elif len(mac_address) == 12 + 5:
        sep = mac_address[2]
        mac_address = mac_address.replace(sep, '')
    else:
        raise ValueError('错误的MAC地址格式')
    send_data = __create_magic_packet(mac_address)
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.sendto(send_data, ('255.255.255.255', 9))  # ok
    
def __create_magic_packet(mac):
    data = b'FF' * 6 + (mac * 16).encode()
    send_data = b''
    for i in range(0, len(data), 2):
        send_data = send_data + struct.pack(b'B', int(data[i: i + 2], 16))
    return send_data

# wake_on_lan('50E549EFCCFC')  #wola唤醒卧室技嘉主板电脑
# wake_on_lan('00E0AAB1362B')  #wol唤醒卧室奔腾CPU电脑

######################################################################

# udp主服务
udp=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #创建udp socket对象
udp.bind(('0.0.0.0',8266))  #wile方便本地局域网调试,也固定一个端口
udp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)   #设置socket复用避免资源耗尽
#GPIO状态心跳包(一包两用,既传心跳也传状态)
def heart(udp):
    while True:
        GPIO_STATE={'gpio1':gpio1.value(),
                    'gpio2':gpio2.value(),
                    'gpio3':gpio3.value(),
                    'gpio4':gpio4.value(),
                    'gpio5':gpio5.value(),
                    'gpio6':gpio6.value()}
        
        udp.sendto('#c#{}#{}'.format(device_name,GPIO_STATE).encode(),server_addr)

        # udp.sendto('#c#{}#{}'.format(device_name,GPIO_STATE).encode(),server_addr)    #采用双心跳机制
        time.sleep(120) #双向心跳包(两个心跳包必须要有一个间隔是在120秒内的,因为UDP保活的机制)

#_thread.start_new_thread(target=heart,args=(udp,)) #注意,这样在micropython是错误的
_thread.start_new_thread(heart,(udp,)) #正确

#开启循环监听
while True:
    try:
        #udp.sendto('#c#{}'.format(device_name).encode(),server_addr)    #客户端注册
        msg,addr=udp.recvfrom(1024)
        print('addr:',addr)
        msg=msg.decode()  #这里存在潜在的错误，需要异常捕获
        print('data:',msg)

        #进度条
        if 'jdt' in msg:
            num=int(msg[3:])
            if 0<=num<=5:    
                gpio1.off()
                gpio2.off()
                gpio3.off()
                gpio5.off()
                udp.sendto('off'.encode(),('192.168.8.66',8266))    #日光灯
                udp.sendto('off2'.encode(),('192.168.8.66',8266))    #夜灯模式
            if 5<num<=18:    
                gpio1.on()
                gpio2.off()
                gpio3.off()
                gpio5.off()
                udp.sendto('off'.encode(),('192.168.8.66',8266))    #日光灯
                udp.sendto('off2'.encode(),('192.168.8.66',8266))    #夜灯模式
            if 18<num<=37:    
                gpio1.on()
                gpio2.off()
                gpio3.off()
                gpio5.off()
                udp.sendto('off'.encode(),('192.168.8.66',8266))    #日光灯
                udp.sendto('on2'.encode(),('192.168.8.66',8266))    #夜灯模式      
            if 37<num<=62:
                gpio2.on()
                gpio1.off()
                gpio3.off()
                gpio5.off()
                udp.sendto('off'.encode(),('192.168.8.66',8266))    #日光灯
                udp.sendto('on2'.encode(),('192.168.8.66',8266))    #夜灯模式 
            if 62<=num<=74:
                gpio3.on()
                gpio1.off()
                gpio2.off()
                gpio5.off()
                udp.sendto('off'.encode(),('192.168.8.66',8266))    #日光灯
                udp.sendto('off2'.encode(),('192.168.8.66',8266))    #夜灯模式
            if 74<num<=87:
                gpio3.on()
                gpio5.on()
                gpio1.off()
                gpio2.off()
                udp.sendto('off'.encode(),('192.168.8.66',8266))    #日光灯
                udp.sendto('off2'.encode(),('192.168.8.66',8266))    #夜灯模式
            if 87<num<=95:
                gpio3.on()
                gpio5.on()
                udp.sendto('on'.encode(),('192.168.8.66',8266))    #日光灯
                udp.sendto('off2'.encode(),('192.168.8.66',8266))    #夜灯模式 
                gpio1.off()
                gpio2.off()
            if 95<num<=100:
                gpio3.on()
                gpio5.on()
                gpio1.on()
                gpio2.on()
                udp.sendto('on'.encode(),('192.168.8.66',8266))    #日光灯
                udp.sendto('on2'.encode(),('192.168.8.66',8266))    #夜灯模式

        #国际电工开关面板接口
        if 'gjdg' in msg:
            numstr=msg[4:]
            if '1' in numstr:   #小夜灯
                gpio1_temp_state=gpio1.value()
                if gpio1_temp_state==0:
                    gpio1.on()
                    udp.sendto('off2'.encode(),('192.168.8.66',8266))    #夜灯模式
                    gpio2.off()
                    gpio3.off()
                    gpio5.off()
                    udp.sendto('off'.encode(),('192.168.8.66',8266))    #日光灯
                else:
                    gpio1.off()
                    gpio2.off()
                    gpio3.off()
                    gpio5.off()
                    udp.sendto('off'.encode(),('192.168.8.66',8266))    #日光灯
                    udp.sendto('off2'.encode(),('192.168.8.66',8266))    #夜灯模式 
            if '2' in numstr:   #大夜灯
                gpio2_temp_state=gpio2.value()
                if yedeng_model==0: #正常模式
                    if gpio2_temp_state==0:
                        gpio2.on()
                        udp.sendto('on2'.encode(),('192.168.8.66',8266))    #夜灯模式
                        gpio3.off()
                        gpio5.off()
                        gpio1.off()
                        udp.sendto('off'.encode(),('192.168.8.66',8266))    #日光灯
                    else:
                        gpio2.off()
                        udp.sendto('off2'.encode(),('192.168.8.66',8266))    #夜灯模式
                        gpio3.off()
                        gpio5.off()
                        gpio1.off()
                        udp.sendto('off'.encode(),('192.168.8.66',8266))    #日光灯
                if yedeng_model==1: #小夜灯模式
                    gpio1_temp_state=gpio1.value()
                    if gpio1_temp_state==0:
                        gpio1.on()
                        udp.sendto('on2'.encode(),('192.168.8.66',8266))    #夜灯模式
                        gpio3.off()
                        gpio5.off()
                        gpio2.off()
                        udp.sendto('off'.encode(),('192.168.8.66',8266))    #日光灯
                    else:
                        gpio1.off()
                        udp.sendto('off2'.encode(),('192.168.8.66',8266))    #夜灯模式
                        gpio3.off()
                        gpio5.off()
                        gpio1.off()
                        udp.sendto('off'.encode(),('192.168.8.66',8266))    #日光灯                

            if '3' in numstr:   #灯带模式
                if '104' in addr[0]:#原灯带按钮改为控制窗帘开关
                    if cl_stat=='0':
                        udp.sendto('on'.encode(),('192.168.8.107',8266)) #开窗帘
                        cl_stat='1'
                    else:
                        udp.sendto('off'.encode(),('192.168.8.107',8266)) #关窗帘
                        cl_stat='0'
                else:
                    gpio3_temp_state=gpio3.value()
                    if gpio3_temp_state==0:
                        gpio3.on()
                        gpio5.on()
                        udp.sendto('on2'.encode(),('192.168.8.66',8266))    #日光灯
                        udp.sendto('off'.encode(),('192.168.8.66',8266))    #日光灯
                    else:
                        gpio3.off()
                        gpio5.off()
                        udp.sendto('off2'.encode(),('192.168.8.66',8266))    #日光灯
                        udp.sendto('off'.encode(),('192.168.8.66',8266))    #日光灯
            if '5' in numstr:   #主灯(衣柜灯带_日光灯)
                gpio5_temp_state=gpio5.value()
                if gpio5_temp_state==0:
                    gpio3.on()
                    gpio5.on()
                    udp.sendto('on'.encode(),('192.168.8.66',8266))    #日光灯
                    udp.sendto('off2'.encode(),('192.168.8.66',8266))    #日光灯
                else:
                    gpio3.off()
                    gpio5.off()
                    udp.sendto('off'.encode(),('192.168.8.66',8266))    #日光灯
                    udp.sendto('off2'.encode(),('192.168.8.66',8266))    #日光灯

        #夜灯模式接口
        if msg=='yd1':   #大夜灯模式
            gpio2.on()
            udp.sendto('on2'.encode(),('192.168.8.66',8266))
            gpio1.off()
            gpio3.off()
            gpio5.off()
            udp.sendto('off'.encode(),('192.168.8.66',8266))
        if msg=='yd2':   #小夜灯模式
            gpio1.on()
            udp.sendto('on2'.encode(),('192.168.8.66',8266))
            gpio2.off()
            gpio3.off()
            gpio5.off()
            udp.sendto('off'.encode(),('192.168.8.66',8266))
            yedeng_model=1
        if msg=='yd3':   #睡前模式
            gpio1.on()
            udp.sendto('off2'.encode(),('192.168.8.66',8266))
            gpio2.off()
            gpio3.off()
            gpio5.off()
            udp.sendto('off'.encode(),('192.168.8.66',8266))
            yedeng_model=1
        if msg=='zc':   #正常模式
            yedeng_model=0
        if msg=='yd':   #夜灯模式
            yedeng_model=1

        #老式版本GPIO接口
        if msg=='led1on':   #开led1
            gpio1.on()
        if msg=='led1off':  #关led1
            gpio1.off()
        if msg=='gpio1on':   #开gpio1
            gpio1.on()
        if msg=='gpio1off':  #关gpio1
            gpio1.off()
        if msg=='ledoff':   #开led1/2
            gpio2.on()
            gpio3.on()
            gpio4.on()
        if msg=='ledon':    #关led1/2
            gpio2.on()
            gpio3.on()                    

        if msg=='allon':    #全开
            pass
            #gpio1.on()
            #gpio2.on()
            #gpio3.on()
            #gpio4.on()
            #gpio5.on()
            #gpio6.on()
        if msg=='alloff':   #全关
            gpio1.off()
            gpio2.off()
            gpio3.off()
            gpio4.off()
            gpio5.off()
            #gpio6.off()
            udp.sendto('off2'.encode(),('192.168.8.66',8266))    #夜灯模式
            udp.sendto('off'.encode(),('192.168.8.66',8266))

        if msg=='get_state':    #获取GPIO状态
            GPIO_STATE={'gpio1':gpio1.value(),
                        'gpio2':gpio2.value(),
                        'gpio3':gpio3.value(),
                        'gpio4':gpio4.value(),
                        'gpio5':gpio5.value(),
                        'gpio6':gpio6.value()}
            udp.sendto('#c#{}#{}'.format(device_name,GPIO_STATE).encode(),addr)
        
        #新版本GPIO接口
        if msg=='all':    #gpio1/2/3  外加台灯
            led1_temp_state=gpio2.value()
            if led1_temp_state==0:
                gpio1.on()
                gpio2.on()
                gpio3.on()
                gpio4.on()
                #gpio5.on()
                #gpio6.on()
                #udp.sendto('on'.encode(),('192.168.8.66',8266))
            else:
                gpio1.off()
                gpio2.off()
                gpio3.off()
                gpio4.off()
                #gpio5.off()
                #gpio6.off()
                #udp.sendto('off'.encode(),('192.168.8.66',8266))

        if msg=='gpio1':    #小夜灯_gpio1单控反转
            gpio1_temp_state=gpio1.value()
            if gpio1_temp_state==0:
                gpio1.on()
            else:
                gpio1.off()

        if msg=='gpio2':    #大夜灯_gpio2单控反转
            gpio2_temp_state=gpio2.value()
            if gpio2_temp_state==0:
                gpio2.on()
            else:
                gpio2.off()

        if msg=='gpio3':    #飘窗灯带_gpio3单控反转
            gpio3_temp_state=gpio3.value()
            if gpio3_temp_state==0:
                gpio3.on()
            else:
                gpio3.off()

        if msg=='gpio4':    #暂未使用_gpio1单控反转
            gpio4_temp_state=gpio4.value()
            if gpio4_temp_state==0:
                gpio4.on()
            else:
                gpio4.off()

        if msg=='gpio5':    #衣柜灯带_gpio2单控反转
            gpio5_temp_state=gpio5.value()
            if gpio5_temp_state==0:
                gpio5.on()
            else:
                gpio5.off()

        if msg=='gpio6':    #音响_gpio3单控反转
            gpio6_temp_state=gpio6.value()
            if gpio6_temp_state==0:
                gpio6.on()
            else:
                gpio6.off()
        if msg=='gpio7':    #日光灯_单控反转
            udp.sendto('led'.encode(),('192.168.8.66',8266))

        if msg=='gpio3on':  #gpio3开启
            gpio3.on()

        if msg=='gpio3off': #gpio3关闭
            gpio3.off()

        # 推送固件升级接口
        if msg=='get_version':    #获取固件版本
            get_version()     
        if msg=='auto_update_firmware':    #检查固件并自动更新
            auto_update_firmware()
        if msg=='update_firmware':    ##强制更新
            update_firmware()#强制更新  
        
        #wol唤醒接口
        #远程开关技嘉主板电脑
        if msg=='jijia_on':
            wake_on_lan('50E549EFCCFC')  #wol唤醒卧室技嘉主板电脑
        if msg=='jijia_off':
            udp.sendto('jijia_off'.encode(),('192.168.8.188',8266)) #远程关机,需要脚本支持
        # #远程开关奔腾主板电脑
        # if msg=='bengteng_on':
        #     wake_on_lan('00E0AAB1362B')  #wol唤醒卧室奔腾CPU电脑
        # if msg=='bengteng_off':
        #     udp.sendto('bengteng_off'.encode(),('192.168.8.166',8266)) #远程关机,需要脚本支持

        #服务器电源控制及开关机键控制
        if msg=='poweron':
            led12.on()
        if msg=='poweroff':
            led12.off()
        if msg=='pcon':
            #led12.on()
            #time.sleep(5)
            led13.on()
            time.sleep(1)
            led13.off()
        if msg=='pcoff':
            led13.on()
            time.sleep(1)
            led13.off()
            #time.sleep(30)
            #led12.off()
    except Exception as e:
        print(e)
        print('重新监听...')

######################################################################

